package iceworld.fernado.file.impl;

import iceworld.fernado.consts.Constants;
import iceworld.fernado.entity.Field;
import iceworld.fernado.resource.SysConfig;
import iceworld.fernado.util.SystemResource;
import iceworld.fernado.util.Utils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class XmlSaveResource extends AbstractSaveResource {

	private static final String ID = XmlSaveResource.class.getName();
	private static final Logger log = Logger.getLogger(ID);
	
	private static final String FILE_TYPE = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
	private static final String folder = SystemResource.getInstance().getProjectFolder() + "generator/";
	private static final String XMLNS = "http://ibatis.apache.org/mapping";
	private static final String XMLNS_XSI = "http://www.w3.org/2001/XMLSchema-instance";
	private static final String XML_HEADER;
	
	private static final String END_BRACKET_WITH_SPLASH = "/>";
	private static final String END_BRACKET_WITH_QUOTE = "\">";
	private static final String START_ALIAS = "<alias>";
	private static final String END_ALIAS = "</alias>";
	private static final String START_RESULT_MAPS = "<resultMaps>";
	private static final String END_RESULT_MAPS = "</resultMaps>";
	private static final String START_RESULT_MAP_WITH_ID_QUOTE = "<resultMap id=\"";
	private static final String START_RESULT_MAP = "resultMap=\"";
	private static final String END_RESULT_MAP = "</resultMap>";
	private static final String START_RESULT = "<result property=\"";
	private static final String START_STATEMENTS = "<statements>";
	private static final String END_STATEMENTS = "</statements>";
	private static final String START_SELECT_WITH_ID_QUOTE = "<select id=\"";
	private static final String END_SELECT = "</select>";
	private static final String START_INSERT_WITH_ID_QUOTE = "<insert id=\"";
	private static final String END_INSERT = "</insert>";
	private static final String START_UPDATE_WITH_ID_QUOTE = "<update id=\"";
	private static final String END_UPDATE = "</update>";
	private static final String START_DELETE_WITH_ID_QUOTE = "<delete id=\"";
	private static final String END_DELETE = "</delete>";
	private static final String PARAMETER_CLASS = "parameterClass=\"";
	private static final String START_TYPE_ALIAS = "<typeAlias ";
	private static final String ALIAS = "alias=\"";
	private static final String TYPE = "type=\"";
	private static final String CLASS = "class=\"";
	private static final String COLUMN = "column=\"";
	private static final String START_SQL_MAP = "<sqlMap ";
	private static final String END_SQL_MAP = "</sqlMap>";
	private static final String SPECIAL_SYMBOL_ONE = "#";
	private static final String TABLE_NICK_NAME = "U";
	
	static {
		StringBuilder builder = new StringBuilder();
		builder.append(START_SQL_MAP);
		builder.append("namespace=\"");
		builder.append(SysConfig.getInstance().getValue("xml_namespacename"));
		builder.append(Constants.ONE_DOUBLE_QUOTE);
		builder.append(Constants.SPACE);
		builder.append("xmlns=\"");
		builder.append(XMLNS);
		builder.append(Constants.ONE_DOUBLE_QUOTE);
		builder.append(Constants.SPACE);
		builder.append("xmlns:xsi=\"");
		builder.append(XMLNS_XSI);
		builder.append(Constants.ONE_DOUBLE_QUOTE);
		builder.append(">");
		XML_HEADER = builder.toString();
	}
	public String getLanguageSuffix() {
		return ".xml";
	}
	
	@Override
	public void removeResources() {
		File file = new File(folder);
		if (file.isDirectory()) {
			File[] files = file.listFiles();
			if (null != files) {
				for (File f: files) {
					if (f.getAbsolutePath().endsWith(getLanguageSuffix())) {
						f.delete();
					}
				}
			}
		}
	}

	@Override
	public void saveResources(String tableName, List<Field> fields) {
		String newTableName = generateTableName(tableName);
		String clazzName = formatClazzName(newTableName);
		writeFile(folder + clazzName, this.generateXMLContent(newTableName, clazzName, fields));
	}
	
	private String generateXMLContent(String tableName, String clazzName, List<Field> fields) {
		StringBuilder result = new StringBuilder();
		result.append(FILE_TYPE);
		result.append(Constants.NEWLINE_TEXT);
		result.append(XML_HEADER);
		result.append(Constants.NEWLINE_TEXT);
		generateAlias(result, clazzName);
		result.append(Constants.NEWLINE_TEXT);
		this.generateResultMaps(result, clazzName, fields);
		this.generateStatements(result, tableName, clazzName, fields);
		result.append(END_SQL_MAP);
		return result.toString();
	}
	
	private void generateAlias(StringBuilder builder, String clazzName) {
		builder.append(Utils.howMany2Spaces(1));
		builder.append(START_ALIAS);
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(2));
		builder.append(START_TYPE_ALIAS);
		builder.append(ALIAS);
		builder.append(clazzName);
		builder.append(Constants.ONE_DOUBLE_QUOTE);
		builder.append(Constants.EMPTY_STRING);
		builder.append(TYPE);
		builder.append(SysConfig.getInstance().getValue("xml_namespacename"));
		builder.append(Constants.COMMA);
		builder.append(SysConfig.getInstance().getValue("xml_namespacename")).append(clazzName);
		builder.append(END_BRACKET_WITH_QUOTE);
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(1));
		builder.append(END_ALIAS);
	}
	
	private void generateResultMaps(StringBuilder builder, String tableName, List<Field> fields) {
		builder.append(Utils.howMany2Spaces(1));
		builder.append(START_RESULT_MAPS);
		builder.append(Constants.NEWLINE_TEXT);
		
		generateResultMap(builder, tableName, fields);
		
		builder.append(Utils.howMany2Spaces(1));
		builder.append(END_RESULT_MAPS);
		builder.append(Constants.NEWLINE_TEXT);
	}
	
	private void generateResultMap(StringBuilder builder, String tableName, List<Field> fields) {
		builder.append(Utils.howMany2Spaces(2));
		builder.append(START_RESULT_MAP_WITH_ID_QUOTE);
		builder.append(tableName);
		builder.append(Constants.ONE_DOUBLE_QUOTE);
		builder.append(Constants.SPACE);
		builder.append(CLASS);
		builder.append(tableName);
		builder.append(END_BRACKET_WITH_QUOTE);
		builder.append(Constants.NEWLINE_TEXT);
		for (Field field: fields) {
			this.generateResult(builder, field);
		}
		builder.append(Utils.howMany2Spaces(2));
		builder.append(END_RESULT_MAP);
		builder.append(Constants.NEWLINE_TEXT);
	}
	
	private void generateResult(StringBuilder builder, Field field) {
		if (!Utils.isEmpty(field.getType())) {
			builder.append(Utils.howMany2Spaces(3));
			builder.append(START_RESULT);
			builder.append(field.getName());
			builder.append(Constants.ONE_DOUBLE_QUOTE);
			builder.append(Constants.SPACE);
			builder.append(COLUMN);
			builder.append(field.getName());
			builder.append(Constants.ONE_DOUBLE_QUOTE);
			builder.append(END_BRACKET_WITH_SPLASH);
			builder.append(Constants.NEWLINE_TEXT);
		}
	}
	
	private void generateStatements(StringBuilder builder, String tableName, String clazzName, List<Field> fields) {
		builder.append(Utils.howMany2Spaces(1));
		builder.append(START_STATEMENTS);
		builder.append(Constants.NEWLINE_TEXT);
		this.generateRetrieveStatement(builder, tableName, clazzName);
		boolean hasPrimaryKey = false;
		for (Field field: fields) {
			if (field.isPrimaryKey()) {
				hasPrimaryKey = true;
				break;
			}
		}
		this.generateRetrieveStatements(builder, tableName, clazzName, fields);
		if (hasPrimaryKey) {
			this.generateRetrieveStatementByPrimaryKeys(builder, tableName, clazzName, fields);
			this.generateCreateStatement(builder, tableName, clazzName, fields);
			this.generateUpdateStatement(builder, tableName, clazzName, fields);
			this.generateDeleteStatement(builder, tableName, clazzName, fields);
		}
		builder.append(Utils.howMany2Spaces(1));
		builder.append(END_STATEMENTS);
		builder.append(Constants.NEWLINE_TEXT);
	}
	
	private void generateRetrieveStatementByPrimaryKeys(StringBuilder builder,
			String tableName, String clazzName, List<Field> fields) {
		builder.append(Utils.howMany2Spaces(2));
		builder.append(START_SELECT_WITH_ID_QUOTE);
		builder.append("get").append(clazzName).append("ByPrimaryKeys");
		builder.append(Constants.ONE_DOUBLE_QUOTE);
		builder.append(Constants.SPACE);
		builder.append(START_RESULT_MAP);
		builder.append(clazzName);
		builder.append(END_BRACKET_WITH_QUOTE);
		builder.append(Constants.NEWLINE_TEXT);
		
		this.generateRetrieveStatementContentByPrimaryKeys(builder, tableName, fields);
		builder.append(Utils.howMany2Spaces(2));
		builder.append(END_SELECT);
		builder.append(Constants.NEWLINE_TEXT);
	}
	
	private void generateRetrieveStatementContentByPrimaryKeys(StringBuilder builder, String tableName, List<Field> fields) {
		builder.append(Utils.howMany2Spaces(3));
		builder.append("SELECT");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		builder.append("*");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(3));
		builder.append("FROM");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		builder.append(tableName);
		builder.append(Constants.SPACE);
		builder.append(TABLE_NICK_NAME);
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(3));
		builder.append("WHERE");
		StringBuilder primaryKeysBuilder = new StringBuilder();
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		for (Field field: fields) {
			if (field.isPrimaryKey()) {
				primaryKeysBuilder.append(field.getName());
				primaryKeysBuilder.append(Constants.EQUAL_SIGN);
				primaryKeysBuilder.append(SPECIAL_SYMBOL_ONE);
				primaryKeysBuilder.append(field.getName());
				primaryKeysBuilder.append(SPECIAL_SYMBOL_ONE);
				primaryKeysBuilder.append(Constants.COMMA);
				primaryKeysBuilder.append(Constants.EMPTY_STRING);
			}
		}
		String primaryKeys = primaryKeysBuilder.length() > 0 
			? primaryKeysBuilder.toString().substring(0, primaryKeysBuilder.length() - 1) 
			: Constants.EMPTY_STRING;
		builder.append(primaryKeys);
		builder.append(Constants.NEWLINE_TEXT);
	}
	
	private void generateRetrieveStatement(StringBuilder builder, String tableName, String clazzName) {
		builder.append(Utils.howMany2Spaces(2));
		builder.append(START_SELECT_WITH_ID_QUOTE);
		builder.append("get").append(clazzName);
		builder.append(Constants.ONE_DOUBLE_QUOTE);
		builder.append(Constants.SPACE);
		builder.append(START_RESULT_MAP);
		builder.append(clazzName);
		builder.append(END_BRACKET_WITH_QUOTE);
		builder.append(Constants.NEWLINE_TEXT);
		
		this.generateRetrieveStatementContent(builder, tableName);
		builder.append(Utils.howMany2Spaces(2));
		builder.append(END_SELECT);
		builder.append(Constants.NEWLINE_TEXT);
	}

	private void generateRetrieveStatementContent(StringBuilder builder, String tableName) {
		builder.append(Utils.howMany2Spaces(3));
		builder.append("SELECT");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		builder.append("*");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(3));
		builder.append("FROM");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		builder.append(tableName);
		builder.append(Constants.NEWLINE_TEXT);
	}
	
	private void generateRetrieveStatements(StringBuilder builder, String tableName, String clazzName, List<Field> fields) {
		for (Field field: fields) {
			if (!Utils.isEmpty(field.getType())) {
				this.generateRetrieveStatement(builder, tableName, field);
			}
		}
	}
	
	private void generateRetrieveStatement(StringBuilder builder, String tableName, Field field) {
		builder.append(Utils.howMany2Spaces(2));
		builder.append(START_SELECT_WITH_ID_QUOTE);
		builder.append("get").append(field.getName());
		builder.append(Constants.ONE_DOUBLE_QUOTE);
		builder.append(Constants.SPACE);
		builder.append(PARAMETER_CLASS);
		builder.append(field.getType());
		builder.append(Constants.ONE_DOUBLE_QUOTE);
		builder.append(Constants.SPACE);
		builder.append(START_RESULT_MAP);
		builder.append(tableName);
		builder.append(END_BRACKET_WITH_QUOTE);
		builder.append(Constants.NEWLINE_TEXT);
		
		this.generateRetrieveStatementContentByField(builder, tableName, field);
		builder.append(Utils.howMany2Spaces(2));
		builder.append(END_SELECT);
		builder.append(Constants.NEWLINE_TEXT);
	}
	
	private void generateRetrieveStatementContentByField(StringBuilder builder, String tableName, Field field) {
		builder.append(Utils.howMany2Spaces(3));
		builder.append("SELECT");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		builder.append("*");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(3));
		builder.append("FROM");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		builder.append(tableName);
		builder.append(Constants.SPACE);
		builder.append("U");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(3));
		builder.append("WHERE");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(this.generateRetrieveStatementContentByFieldCondition(builder, tableName, field));
		builder.append(Constants.NEWLINE_TEXT);
	}
	
	private String generateRetrieveStatementContentByFieldCondition(StringBuilder builder, String tableName, Field field) {
		StringBuilder result = new StringBuilder();
		result.append(Utils.howMany2Spaces(4));
		result.append(TABLE_NICK_NAME);
		result.append(Constants.DOT);
		result.append(field.getName());
		result.append(Constants.EQUAL_SIGN);
		result.append(SPECIAL_SYMBOL_ONE);
		result.append(field.getName());
		result.append(SPECIAL_SYMBOL_ONE);
		result.append(Constants.COMMA);
		return result.length() > 0 
					? result.toString().substring(0, result.length() - 1) 
					: Constants.EMPTY_STRING;
	}
	
	private void generateCreateStatement(StringBuilder builder, String tableName, String clazzName, List<Field> fields) {
		builder.append(Utils.howMany2Spaces(2));
		builder.append(START_INSERT_WITH_ID_QUOTE);
		builder.append("create").append(clazzName);
		builder.append(Constants.ONE_DOUBLE_QUOTE);
		builder.append(Constants.SPACE);
		builder.append(PARAMETER_CLASS);
		builder.append(clazzName);
		builder.append(END_BRACKET_WITH_QUOTE);
		builder.append(Constants.NEWLINE_TEXT);
		
		this.generateCreateStatementContent(builder, tableName, fields);
		
		builder.append(Utils.howMany2Spaces(2));
		builder.append(END_INSERT);
		builder.append(Constants.NEWLINE_TEXT);
		
	}
	
	private void generateCreateStatementContent(StringBuilder builder, String tableName, List<Field> fields) {
		builder.append(Utils.howMany2Spaces(3));
		builder.append("INSERT INTO ");
		builder.append(tableName);
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		builder.append("(");
		builder.append(this.generateCreateFields(fields));
		builder.append(")");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(3));
		builder.append("VALUES");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		builder.append("(");
		builder.append(this.generateCreateFieldsWithSpecialSymbol(fields));
		builder.append(")");
		builder.append(Constants.NEWLINE_TEXT);
	}
	
	private String generateCreateFields(List<Field> fields) {
		StringBuilder result = new StringBuilder();
		for (Field field: fields) {
			if (!Utils.isEmpty(field.getType())) {
				result.append(field.getName());
				result.append(Constants.COMMA);
				result.append(Constants.EMPTY_STRING);
			}
		}
		return result.length() > 0 
			? result.toString().substring(0, result.length() - 1) 
			: Constants.EMPTY_STRING;
	}
	
	private String generateCreateFieldsWithSpecialSymbol(List<Field> fields) {
		StringBuilder result = new StringBuilder();
		for (Field field: fields) {
			if (!Utils.isEmpty(field.getType())) {
				result.append(SPECIAL_SYMBOL_ONE);
				result.append(field.getName());
				result.append(SPECIAL_SYMBOL_ONE);
				result.append(Constants.COMMA);
				result.append(Constants.EMPTY_STRING);
			}
		}
		return result.length() > 0 
			? result.toString().substring(0, result.length() - 1) 
			: Constants.EMPTY_STRING;
	}
	
	private void generateUpdateStatement(StringBuilder builder, String tableName, String clazzName, List<Field> fields) {
		builder.append(Utils.howMany2Spaces(2));
		builder.append(START_UPDATE_WITH_ID_QUOTE);
		builder.append("update").append(clazzName);
		builder.append(Constants.ONE_DOUBLE_QUOTE);
		builder.append(Constants.SPACE);
		builder.append(PARAMETER_CLASS);
		builder.append(clazzName);
		builder.append(END_BRACKET_WITH_QUOTE);
		builder.append(Constants.NEWLINE_TEXT);
		
		this.generateUpdateStatementContent(builder, tableName, fields);
		
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(2));
		builder.append(END_UPDATE);
		builder.append(Constants.NEWLINE_TEXT);
	}
	
	private void generateUpdateStatementContent(StringBuilder builder, String tableName, List<Field> fields) {
		builder.append(Utils.howMany2Spaces(3));
		builder.append("UPDATE ");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		builder.append(tableName);
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(3));
		builder.append("SET ");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		builder.append(this.generateUpdateFieldsWithSpecialSymbol(fields));
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(3));
		builder.append("WHERE ");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		builder.append(this.generatePrimaryKeysWithSpecialSymbol(fields));
	}
	
	private String generateUpdateFieldsWithSpecialSymbol(List<Field> fields) {
		StringBuilder result = new StringBuilder();
		for (Field field: fields) {
			if (!Utils.isEmpty(field.getType())) {
				result.append(field.getName());
				result.append(Constants.EQUAL_SIGN);
				result.append(SPECIAL_SYMBOL_ONE);
				result.append(field.getName());
				result.append(SPECIAL_SYMBOL_ONE);
				result.append(Constants.COMMA);
				result.append(Constants.EMPTY_STRING);
			}
		}
		return result.length() > 0 
			? result.toString().substring(0, result.length() - 1) 
			: Constants.EMPTY_STRING;
	}
	
	private void generateDeleteStatement(StringBuilder builder, String tableName, String clazzName, List<Field> fields) {
		builder.append(Utils.howMany2Spaces(2));
		builder.append(START_DELETE_WITH_ID_QUOTE);
		builder.append("delete");
		builder.append(clazzName);
		builder.append(Constants.ONE_DOUBLE_QUOTE);
		builder.append(Constants.SPACE);
		builder.append(PARAMETER_CLASS);
		builder.append(clazzName);
		builder.append(END_BRACKET_WITH_QUOTE);
		builder.append(Constants.NEWLINE_TEXT);
		
		this.generateDeleteStatementContent(builder, tableName, fields);
		
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(2));
		builder.append(END_DELETE);
		builder.append(Constants.NEWLINE_TEXT);
	}
	
	private void generateDeleteStatementContent(StringBuilder builder, String tableName, List<Field> fields) {
		builder.append(Utils.howMany2Spaces(3));
		builder.append("DELETE FROM ");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		builder.append(tableName);
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(3));
		builder.append("WHERE ");
		builder.append(Constants.NEWLINE_TEXT);
		builder.append(Utils.howMany2Spaces(4));
		builder.append(this.generatePrimaryKeysWithSpecialSymbol(fields));
	}
	
	private String generatePrimaryKeysWithSpecialSymbol(List<Field> fields) {
		StringBuilder result = new StringBuilder();
		for (Field field: fields) {
			if (field.isPrimaryKey()) {
				result.append(field.getName());
				result.append(Constants.EQUAL_SIGN);
				result.append(SPECIAL_SYMBOL_ONE);
				result.append(field.getName());
				result.append(SPECIAL_SYMBOL_ONE);
				result.append(Constants.COMMA);
				result.append(Constants.EMPTY_STRING);
			}
		}
		return result.length() > 0 
			? result.toString().substring(0, result.length() - 1) 
			: Constants.EMPTY_STRING;
	}
	
	private void writeFile(String filename, String content) {
		FileWriter fw = null;
		try {
			fw = new FileWriter(new File(filename + getLanguageSuffix()), true);
			fw.write(content);
		} catch (IOException e) {
			log.log(Level.INFO, "CANT OPEN FILE - " + filename + getLanguageSuffix());
		} finally {
			if (null != fw) {
				try {
					fw.close();
				} catch (IOException e) {
					log.log(Level.INFO, "CANT CLOSE FILE - " + filename + getLanguageSuffix());
				}
			}
		}
	}

}
